package com.zg.scf.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zg.common.core.dao.auth.*;
import com.zg.common.core.dao.scf.CompanyDao;
import com.zg.common.core.dao.scf.ScfRoleExtDao;
import com.zg.common.core.dao.scf.ScfUserCompanyDao;
import com.zg.common.core.dao.scf.entity.Company;
import com.zg.common.core.dao.scf.entity.ScfRoleExt;
import com.zg.common.core.dao.scf.entity.ScfUserCompany;
import com.zg.common.core.exception.BusinessException;
import com.zg.common.web.Req;
import com.zg.scf.constant.ScfE;
import com.zg.scf.enm.CompanyInfoConstants;
import com.zg.scf.enm.UserTypeEnum;
import com.zg.scf.domain.req.*;
import com.zg.scf.domain.vo.ScfRoleVo;
import com.zg.scf.domain.vo.ScfUserVo;
import com.zg.scf.service.ScfUserService;
import com.zg.scf.util.DataScopeUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ScfUserServiceImpl implements ScfUserService {

    private final AuthUserDao authUserDao;
    private final ScfUserCompanyDao scfUserCompanyDao;
    private final AuthRoleUserDao authRoleUserDao;
    private final AuthRoleDao authRoleDao;
    private final DataScopeUtil dataScopeUtil;
    private final ScfRoleExtDao scfRoleExtDao;
    private final CompanyDao companyDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(ScfUserAddReq req) {
        if (!req.getPassword().equals(req.getConfirmPassword())) {
            BusinessException.happen(ScfE.USER_PASSWORD_MISMATCH.getMsg());
        }
        if (authUserDao.exists(
                new LambdaQueryWrapper<AuthUser>()
                        .eq(AuthUser::getUsername, req.getUsername())
        )) {
            BusinessException.happen(ScfE.USER_USERNAME_TAKEN.getMsg());
        }
        if (authUserDao.exists(
                new LambdaQueryWrapper<AuthUser>()
                        .eq(AuthUser::getPhone, req.getPhone())
        )) {
            BusinessException.happen(ScfE.USER_PHONE_TAKEN.getMsg());
        }
        String currUserType = Req.getCurrUser().getType();
        String type = req.getType();
        Long companyId = req.getCompanyId();
        List<Long> roleIds = req.getRoleIds();
        boolean isSystemUser = UserTypeEnum.SYSTEM.getCode().equals(currUserType);
        if (isSystemUser) {
            if (type.equals(currUserType)) {
                companyId = null;
                List<ScfRoleExt> scfRoleExtList = scfRoleExtDao.list(
                        new LambdaQueryWrapper<ScfRoleExt>()
                                .ne(ScfRoleExt::getType, UserTypeEnum.SYSTEM.getCode())
                                .in(ScfRoleExt::getRoleId, roleIds)
                );
                if (!scfRoleExtList.isEmpty()) {
                    BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
                }
            } else {
                if (companyId == null) {
                    BusinessException.happen(ScfE.USER_INVALID_COMPANY.getMsg());
                }
                Company company = companyDao.getById(companyId);
                if (company == null) {
                    BusinessException.happen(ScfE.USER_INVALID_COMPANY.getMsg());
                }
                if (!type.equals(company.getCompanyType())) {
                    BusinessException.happen(ScfE.USER_INVALID_USER_TYPE.getMsg());
                }
                List<ScfRoleExt> scfRoleExtList = scfRoleExtDao.list(
                        new LambdaQueryWrapper<ScfRoleExt>()
                                .eq(ScfRoleExt::getCompanyId, companyId)
                                .in(ScfRoleExt::getRoleId, roleIds));
                if (scfRoleExtList.isEmpty()) {
                    BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
                }
                roleIds = scfRoleExtList.stream().map(ScfRoleExt::getCompanyId).collect(Collectors.toList());
            }
        } else {
            if (!type.equals(currUserType)) {
                BusinessException.happen(ScfE.USER_INVALID_USER_TYPE.getMsg());
            }
            companyId = dataScopeUtil.getOrgDataScope();
            List<ScfRoleExt> scfRoleExtList = scfRoleExtDao.list(
                    new LambdaQueryWrapper<ScfRoleExt>()
                            .eq(ScfRoleExt::getCompanyId, companyId)
                            .in(ScfRoleExt::getRoleId, roleIds));
            if (scfRoleExtList.isEmpty()) {
                BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
            }
            roleIds = scfRoleExtList.stream().map(ScfRoleExt::getCompanyId).collect(Collectors.toList());
        }
        List<AuthRole> authRoleList = authRoleDao.list(
                new LambdaQueryWrapper<AuthRole>()
                        .in(AuthRole::getId, roleIds)
        );
        if (authRoleList.isEmpty()) {
            BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
        }
        roleIds = authRoleList.stream().map(AuthRole::getId).collect(Collectors.toList());
        AuthUser authUser = BeanUtil.toBean(req, AuthUser.class);
        authUser.setType(type);
        authUser.setSearchStr(authUser.getUsername() + authUser.getPhone());
        authUserDao.save(authUser);
        if(companyId != null) {
            ScfUserCompany scfUserCompany = new ScfUserCompany();
            scfUserCompany.setUserId(authUser.getId());
            scfUserCompany.setCompanyId(companyId);
            scfUserCompanyDao.save(scfUserCompany);
        }
        List<AuthRoleUser> authRoleUsers = new ArrayList<>();
        for (Long roleId : roleIds) {
            AuthRoleUser authRoleUser = new AuthRoleUser();
            authRoleUser.setUserId(authUser.getId());
            authRoleUser.setRoleId(roleId);
            authRoleUsers.add(authRoleUser);
        }
        authRoleUserDao.saveBatch(authRoleUsers);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(ScfUserEditReq req) {
        AuthUser authUser = authUserDao.getById(req.getId());
        if (authUser == null) {
            BusinessException.happen(ScfE.USER_INVALID_USER.getMsg());
        }

        if (!authUser.getUsername().equals(req.getUsername()) && authUserDao.exists(
                new LambdaQueryWrapper<AuthUser>()
                        .eq(AuthUser::getUsername, req.getUsername())
        )) {
            BusinessException.happen(ScfE.USER_USERNAME_TAKEN.getMsg());
        }
        if (!authUser.getPhone().equals(req.getPhone()) && authUserDao.exists(
                new LambdaQueryWrapper<AuthUser>()
                        .eq(AuthUser::getPhone, req.getPhone())
        )) {
            BusinessException.happen(ScfE.USER_PHONE_TAKEN.getMsg());
        }

        String currUserType = Req.getCurrUser().getType();
        String type = req.getType();
        Long companyId = req.getCompanyId();
        List<Long> roleIds = req.getRoleIds();
        boolean isSystemUser = UserTypeEnum.SYSTEM.getCode().equals(currUserType);
        if (isSystemUser) {
            if (type.equals(currUserType)) {
                companyId = null;
                List<ScfRoleExt> scfRoleExtList = scfRoleExtDao.list(
                        new LambdaQueryWrapper<ScfRoleExt>()
                                .in(ScfRoleExt::getRoleId, roleIds));
                if (!scfRoleExtList.isEmpty()) {
                    BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
                }
            } else {
                if (companyId == null) {
                    BusinessException.happen(ScfE.USER_INVALID_COMPANY.getMsg());
                }
                Company company = companyDao.getById(companyId);
                if (company == null) {
                    BusinessException.happen(ScfE.USER_INVALID_COMPANY.getMsg());
                }
                if (!type.equals(company.getCompanyType())) {
                    BusinessException.happen(ScfE.USER_INVALID_USER_TYPE.getMsg());
                }
                List<ScfRoleExt> scfRoleExtList = scfRoleExtDao.list(
                        new LambdaQueryWrapper<ScfRoleExt>()
                                .eq(ScfRoleExt::getCompanyId, companyId)
                                .in(ScfRoleExt::getRoleId, roleIds));
                if (scfRoleExtList.isEmpty()) {
                    BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
                }
                roleIds = scfRoleExtList.stream().map(ScfRoleExt::getCompanyId).collect(Collectors.toList());
            }
        } else {
            if (!type.equals(currUserType)) {
                BusinessException.happen(ScfE.USER_INVALID_USER_TYPE.getMsg());
            }
            companyId = dataScopeUtil.getOrgDataScope();
            List<ScfRoleExt> scfRoleExtList = scfRoleExtDao.list(
                    new LambdaQueryWrapper<ScfRoleExt>()
                            .eq(ScfRoleExt::getCompanyId, companyId)
                            .in(ScfRoleExt::getRoleId, roleIds));
            if (scfRoleExtList.isEmpty()) {
                BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
            }
            roleIds = scfRoleExtList.stream().map(ScfRoleExt::getCompanyId).collect(Collectors.toList());
        }
        List<AuthRole> authRoleList = authRoleDao.list(
                new LambdaQueryWrapper<AuthRole>()
                        .in(AuthRole::getId, roleIds)
        );
        if (authRoleList.isEmpty()) {
            BusinessException.happen(ScfE.USER_INVALID_ROLE.getMsg());
        }
        roleIds = authRoleList.stream().map(AuthRole::getId).collect(Collectors.toList());
        BeanUtil.copyProperties(req, authUser);
        authUser.setType(type);
        authUser.setSearchStr(authUser.getUsername() + authUser.getPhone());
        authUserDao.updateById(authUser);

        scfUserCompanyDao.remove(new LambdaQueryWrapper<ScfUserCompany>().eq(ScfUserCompany::getUserId, req.getId()));
        if(companyId != null) {
            ScfUserCompany scfUserCompany = new ScfUserCompany();
            scfUserCompany.setUserId(authUser.getId());
            scfUserCompany.setCompanyId(companyId);
            scfUserCompanyDao.save(scfUserCompany);
        }

        List<AuthRoleUser> authRoleUsers = new ArrayList<>();
        for (Long roleId : roleIds) {
            AuthRoleUser authRoleUser = new AuthRoleUser();
            authRoleUser.setUserId(authUser.getId());
            authRoleUser.setRoleId(roleId);
            authRoleUsers.add(authRoleUser);
        }
        authRoleUserDao.remove(new LambdaQueryWrapper<AuthRoleUser>().eq(AuthRoleUser::getUserId, req.getId()));
        authRoleUserDao.saveBatch(authRoleUsers);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(List<Long> idList) {
        List<AuthUser> authUserList = authUserDao.list(
                new LambdaQueryWrapper<AuthUser>()
                        .in(AuthUser::getId, idList)
        );
        String currUserType = Req.getCurrUser().getType();
        if (!UserTypeEnum.SYSTEM.getCode().equals(currUserType)) {
            for (AuthUser authUser : authUserList) {
                if (!authUser.getType().equals(currUserType)) {
                    BusinessException.happen("没有当前数据类型的操作权限");
                }
            }

            List<ScfUserCompany> scfUserCompanyList = scfUserCompanyDao.list();
            Long orgDataScope = dataScopeUtil.getOrgDataScope();
            for (ScfUserCompany scfUserCompany : scfUserCompanyList) {
                if (!scfUserCompany.getCompanyId().equals(orgDataScope)) {
                    BusinessException.happen("没有当前数据的操作权限");
                }
            }
        }

        authUserDao.removeBatchByIds(idList);
        scfUserCompanyDao.remove(
                new LambdaQueryWrapper<ScfUserCompany>()
                        .in(ScfUserCompany::getUserId, idList)
        );
    }

    @Override
    public IPage<ScfUserVo> page(ScfUserPageReq req) {
        Long orgDataScope = dataScopeUtil.getOrgDataScope();
        List<Long> companyIds = new ArrayList<>();
        String companyName = req.getCompanyName();
        if (StrUtil.isNotBlank(companyName) && UserTypeEnum.SYSTEM.getCode().equals(Req.getCurrUser().getType())) {
            // 查询融资企业/金融机构
            List<Company> companyList = companyDao.list(
                    new LambdaQueryWrapper<Company>()
                            .eq(orgDataScope != null, Company::getId, orgDataScope)
                            .like(Company::getCompanyName, companyName)
            );
            companyIds = companyList.stream().map(Company::getId).collect(Collectors.toList());
            if (companyIds.isEmpty()) {
                return new Page<>(req.getPage(), req.getPageSize());
            }
        }

        Map<Long, Long> userIdMap = new HashMap<>();
        if (orgDataScope != null || !companyIds.isEmpty()) {
            List<ScfUserCompany> scfUserCompanyList = scfUserCompanyDao.list(
                    new LambdaQueryWrapper<ScfUserCompany>()
                            .eq(orgDataScope != null, ScfUserCompany::getCompanyId, orgDataScope)
                            .in(!companyIds.isEmpty(), ScfUserCompany::getCompanyId, companyIds)
            );
            if (scfUserCompanyList.isEmpty()) {
                return new Page<>(req.getPage(), req.getPageSize());
            }
            userIdMap = scfUserCompanyList.stream()
                    .collect(Collectors.toMap(ScfUserCompany::getUserId, ScfUserCompany::getCompanyId));
        }

        IPage<AuthUser> page = authUserDao.page(
                new Page<>(req.getPage(), req.getPageSize()),
                new LambdaQueryWrapper<AuthUser>()
                        .like(StrUtil.isNotBlank(req.getUsername()), AuthUser::getUsername, "%"+req.getUsername()+"%")
                        .like(StrUtil.isNotBlank(req.getPhone()), AuthUser::getPhone, "%"+req.getPhone()+"%")
                        .in(!userIdMap.isEmpty(), AuthUser::getId, userIdMap.keySet())
        );
        if (page.getRecords().isEmpty()) {
            return new Page<>(req.getPage(), req.getPageSize());
        }
        IPage<ScfUserVo> result = new Page<>();
        BeanUtil.copyProperties(page, result);
        result.setRecords(BeanUtil.copyToList(page.getRecords(), ScfUserVo.class));
        Map<Long, ScfUserVo> scfUserMap = result.getRecords().stream()
                .collect(Collectors.toMap(ScfUserVo::getId, Function.identity()));

        // 所属企业
        List<ScfUserCompany> scfUserCompanyList = scfUserCompanyDao.list(
                new LambdaQueryWrapper<ScfUserCompany>()
                        .in(ScfUserCompany::getUserId, scfUserMap.keySet())
        );
        if (!scfUserCompanyList.isEmpty()) {
            Map<Long, List<ScfUserCompany>> companyIdMap = scfUserCompanyList.stream()
                    .collect(Collectors.groupingBy(ScfUserCompany::getCompanyId));
            List<Company> companyList = companyDao.list(
                    new LambdaQueryWrapper<Company>()
                            .in(Company::getId, companyIdMap.keySet())
            );
            for (Company company : companyList) {
                Long companyId = company.getId();
                List<ScfUserCompany> scfUserCompanies = companyIdMap.get(companyId);
                if (scfUserCompanies.isEmpty()) {
                    break;
                }
                for (ScfUserCompany scfUserCompany : scfUserCompanies) {
                    Long userId = scfUserCompany.getUserId();
                    scfUserMap.get(userId).setCompany(company);
                }
            }
        }

        // 关联角色
        List<AuthRoleUser> authRoleUserList = authRoleUserDao.list(
                new LambdaQueryWrapper<AuthRoleUser>()
                        .in(AuthRoleUser::getUserId, scfUserMap.keySet())
        );
        if (authRoleUserList.isEmpty()) {
            return result;
        }
        Map<Long, List<AuthRoleUser>> roleIdMap = authRoleUserList.stream()
                .collect(Collectors.groupingBy(AuthRoleUser::getRoleId));
        List<AuthRole> authRoleList = authRoleDao.list(
                new LambdaQueryWrapper<AuthRole>()
                        .in(AuthRole::getId, roleIdMap.keySet())
        );
        for (AuthRole authRole : authRoleList) {
            Long roleId = authRole.getId();
            List<AuthRoleUser> authRoleUsers = roleIdMap.get(roleId);
            for (AuthRoleUser authRoleUser : authRoleUsers) {
                Long userId = authRoleUser.getUserId();
                ScfUserVo scfUserVo = scfUserMap.get(userId);
                List<ScfRoleVo> roleList = scfUserVo.getRoleList();
                if (roleList == null) {
                    roleList = new ArrayList<>();
                }
                roleList.add(BeanUtil.copyProperties(authRole, ScfRoleVo.class));
                scfUserVo.setRoleList(roleList);
            }
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(ScfUserRegisterReq req) {
        // TODO 验证验证码

        if (!req.getPassword().equals(req.getConfirmPassword())) {
            BusinessException.happen("密码和确认密码不一致");
        }

        if (companyDao.exists(
                new LambdaQueryWrapper<Company>()
                        .eq(Company::getCreditCode, req.getCreditCode())
        )) {
            BusinessException.happen("统一社会信用编码已存在");
        }

        if (companyDao.exists(
                new LambdaQueryWrapper<Company>()
                        .eq(Company::getCompanyName, req.getCompanyName())
        )) {
            BusinessException.happen("企业名称已存在");
        }

        if (authUserDao.exists(
                new LambdaQueryWrapper<AuthUser>()
                        .eq(AuthUser::getPhone, req.getPhone())
        )) {
            BusinessException.happen("手机号码已存在");
        }
        if (authUserDao.exists(
                new LambdaQueryWrapper<AuthUser>()
                        .eq(AuthUser::getUsername, req.getUsername())
        )) {
            BusinessException.happen("账号名已存在");
        }

        AuthUser authUser = new AuthUser();
        authUser.setUsername(req.getUsername());
        authUser.setPhone(req.getPhone());
        authUser.setPassword(req.getPassword());
        authUser.setType(UserTypeEnum.INSTITUTION.getCode());
        authUserDao.save(authUser);

        Company company = new Company();
        company.setCreditCode(req.getCreditCode());
        company.setCompanyName(req.getCompanyName());
        company.setAuthStatus(CompanyInfoConstants.AuthStatusEnum.NOT_STATUS.getValue());
        companyDao.save(company);

        ScfUserCompany scfUserCompany = new ScfUserCompany();
        scfUserCompany.setUserId(authUser.getId());
        scfUserCompany.setCompanyId(company.getId());
        scfUserCompanyDao.save(scfUserCompany);

        List<ScfRoleExt> scfRoleExtList = scfRoleExtDao.list(
                new LambdaQueryWrapper<ScfRoleExt>()
                        .eq(ScfRoleExt::getType, UserTypeEnum.INSTITUTION.getCode())
                        .eq(ScfRoleExt::getAuthStatus, CompanyInfoConstants.AuthStatusEnum.NOT_STATUS.getValue())
                        .isNull(ScfRoleExt::getCompanyId)
        );
        List<AuthRoleUser> authRoleUserList = new ArrayList<>();
        for (ScfRoleExt scfRoleExt : scfRoleExtList) {
            AuthRoleUser authRoleUser = new AuthRoleUser();
            authRoleUser.setRoleId(scfRoleExt.getRoleId());
            authRoleUser.setUserId(authUser.getId());
            authRoleUserList.add(authRoleUser);
        }
        authRoleUserDao.saveBatch(authRoleUserList);
    }
}
